// Serial.cpp: implementation of the CSerial class.
//
//////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <termios.h>
#include "Serial.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

typedef struct port_info
{
	int baud_rate;
	int port_fd;
	char parity;
	char stop_bit;
	char flow_ctrl;
	char data_bits;
}*pport_info;
//get the baud_rate defination according to the baud rate
int get_baud_rate(unsigned long int baud_rate)
{
	switch(baud_rate)
	{
		case 0:
			return B0;
		case 50:
			return B50;
		case 75:
			return B75;
		case 110:
			return B110;
		case 134:
			return B134;
		case 150:
			return B150;
		case 200:
			return B200;
		case 300:
			return B300;
		case 600:
			return B600;
		case 1200:
			return B1200;
		case 1800:
			return B1800;
		case 2400:
			return B2400;
		case 4800:
			return B4800;
		case 9600:
			return B9600;
		case 19200:
			return B19200;
		case 38400:
		    return B38400;
		case 57600:
		    return B57600;
	 	case 115200:
	   		return B115200;
		case 230400:
			return B230400;

		default:
			return -1;
	}
}

int set_port(pport_info p_info)
{
	struct termios new_opt;
	int baud_rate;
	int status;

	//get the current config -> new_opt
	tcgetattr(p_info->port_fd,&new_opt);
	bzero( &new_opt, sizeof(new_opt));

	//convert baud rate -> baud_flag
    baud_rate=get_baud_rate(p_info->baud_rate);

	tcflush(p_info->port_fd, TCIOFLUSH);
	//setup input/output baudrate
	cfsetispeed(&new_opt,baud_rate);
	//printf("cfsetispeed::c_cflag = %x\r\n", new_opt.c_cflag);
	cfsetospeed(&new_opt,baud_rate);
	//printf("cfsetospeed::c_cflag = %x\r\n", new_opt.c_cflag);
	status = tcsetattr(p_info->port_fd, TCSANOW, &new_opt);
	if (status != 0)
	{
		perror("tcsetattr::set baud rate failed\n");
		return -1;
	}

	//�޸Ŀ���ģʽ����֤���򲻻�ռ�ô��ڣ�
	new_opt.c_cflag |= CLOCAL;
	//printf("c_cflag |= CLOCAL => %x\r\n", new_opt.c_cflag);

	//�޸Ŀ���ģʽ��ʹ���ܹ��Ӵ��ڶ�ȡ��������
	new_opt.c_cflag |= CREAD;
	//printf("c_cflag |= CREAD => %x\r\n", new_opt.c_cflag);

	new_opt.c_cflag |= HUPCL;
	//setup control flow
	switch(p_info->flow_ctrl)
	{
	case '0':
		//no control-flow
		new_opt.c_cflag &=~CRTSCTS;
		break;
	case '1':
		//hardware control-flow
		new_opt.c_cflag |=CRTSCTS;
		break;
	case '2':
		new_opt.c_iflag |= IXON | IXOFF | IXANY;
		break;
	}
	//printf("c_cflag(no ctl-flow) = %x\r\n", new_opt.c_cflag);

	//setup bit size
	new_opt.c_cflag &=~CSIZE;
	switch(p_info->data_bits)
	{
	case '5':
		new_opt.c_cflag |=CS5;
		break;
	case '6':
		new_opt.c_cflag |=CS6;
		break;
	case '7':
		new_opt.c_cflag |=CS7;
		break;
	case '8':
		new_opt.c_cflag |=CS8;
		break;
	default:
		new_opt.c_cflag |=CS8;
	}
	printf("c_cflag |= CS8 => %x\r\n", new_opt.c_cflag);

	//setup parity
	switch(p_info->parity)
	{
	case 'n':
	case 'N':
		new_opt.c_cflag &= ~PARENB;   /* Clear parity enable */
		new_opt.c_iflag &= ~INPCK;     /* Disable parity checking */
		break;

	case 'o':
	case 'O':
		new_opt.c_cflag |= (PARODD | PARENB);	/* ����Ϊ��Ч��*/
		new_opt.c_iflag |= INPCK;				/* Enable parity checking */
		break;

	case 'e':
	case 'E':
		new_opt.c_cflag |= PARENB;		/* Enable parity */
		new_opt.c_cflag &= ~PARODD;		/* ת��ΪżЧ��*/
		new_opt.c_iflag |= INPCK;       /* Enable parity checking */
		break;

	case 'S':
	case 's':  /*as no parity*/
		new_opt.c_cflag &= ~PARENB;
		new_opt.c_cflag &= ~CSTOPB;
		break;

	default:
		fprintf(stderr,"Unsupported parity\n");
		return -1;
	}
	//printf("c_cflag &=~PARENB => %x\r\n", new_opt.c_cflag);


	//setup stop-bit
	if(p_info->stop_bit=='2')
	{
		new_opt.c_cflag |=CSTOPB;
	}
	else
	{
		new_opt.c_cflag &=~CSTOPB;
	}
	//printf("c_cflag &=~CSTOPB => %x\r\n", new_opt.c_cflag);

	/* Set input parity option */
	if ((p_info->parity != 'n') || (p_info->parity != 'N'))
	{
		new_opt.c_iflag |= INPCK;
	}

	//�޸����ģʽ��ԭʼ�������(raw ģʽ)
	new_opt.c_lflag &= ~(ICANON | ECHO | ISIG);				/*Input*/
	new_opt.c_oflag &= ~OPOST;								/*Output*/

	//�޸Ŀ����ַ�����ȡ�ַ������ٸ���Ϊ1 ������
	new_opt.c_cc[VMIN]=1;

	//�޸Ŀ����ַ�����ȡ��һ���ַ��ĳ�ʱʱ��Ϊ1��100ms
	new_opt.c_cc[VTIME]=1;

	//��ͼȥ���ڽ���ʱ�����յ�'\n'�ŷ��ص�����
	//��������Ļس�
	//new_opt.c_iflag |= IGNCR;
	//new_opt.c_iflag &= ~(IXON|IXOFF|IXANY);

	//�����������������������ݣ����ǲ��ٶ�ȡ
	tcflush(p_info->port_fd,TCIFLUSH);

	status = tcsetattr(p_info->port_fd,TCSANOW,&new_opt);
	if(status != 0)
	{
		perror("Cannot set the serial port parameters");
		return -1;
	}

	return status;
}

CSerial::CSerial()
{
	m_DatLen = 0;
	m_ExitThreadFlag = 0;
}

CSerial::~CSerial()
{
}

int CSerial::ReceiveThreadFunc(void* lparam)
{
	CSerial *pSer = (CSerial*)lparam;

	//������¼�����
	fd_set fdRead;
	int ret;
	struct timeval	aTime;

	while( 1 )
	{
        //�յ��˳��¼��������߳�
		if( pSer->m_ExitThreadFlag )
		{
			printf("break111\n");
			break;
		}

		FD_ZERO(&fdRead);
		FD_SET(pSer->m_fd,&fdRead);

		aTime.tv_sec = 0;
		aTime.tv_usec = 300000;

		ret = select( pSer->m_fd+1,&fdRead,NULL,NULL,&aTime );
		//printf( "select ret = %d\n", ret);

		if (ret < 0 )
		{
			//�رմ���
			pSer->ClosePort( );
			printf("break222\n");
			break;
		}

		if (ret > 0)
		{
			//�ж��Ƿ���¼�
			if (FD_ISSET(pSer->m_fd,&fdRead))
			{
				//data available, so get it!
				pSer->m_DatLen = 0;
				int tempLen = read( pSer->m_fd, pSer->DatBuf + pSer->m_DatLen, 100 );

				while(tempLen>0)
				{
					pSer->m_DatLen += tempLen;
					usleep(50);
					tempLen = read( pSer->m_fd, pSer->DatBuf + pSer->m_DatLen, 100 );					
				}
				// �Խ��յ����ݽ��д���������Ϊ�򵥵����ݻط�
				if( pSer->m_DatLen > 0 )
				{                                        //printf( "recv data %02x\n",pSer->DatBuf[0]);
					pSer->PackagePro( pSer->DatBuf, pSer->m_DatLen);
				}
				// �������
			}
		}
	}

	printf( "ReceiveThreadFunc finished\n");
	pthread_exit( NULL );
	return 0;
}

int CSerial::OpenPort( int PortNo, int baudrate, char databits, char stopbits, char parity  )
{
	char				portname[20];
	struct port_info 	info;
	pthread_attr_t 		attr;
	int					res;

	sprintf( portname, "/dev/ttyS%d", PortNo );
	//sprintf( portname, "/dev/ttyUSB%d", PortNo );
	//sprintf( portname, "/dev/ttyISA%d", PortNo );
	printf("port name:%s\n",portname);
	if((m_fd = open(portname,O_RDWR | O_NOCTTY | O_NONBLOCK))==-1)			//O_RDWR | O_NOCTTY | O_NDELAY |
	{
		perror("Cannot open the desired port");
		return -1;
	}

    //
    // Fill in the device control block.
    //
 	// �޸���ز���
	info.baud_rate = baudrate;
	info.parity = parity;
	info.data_bits = databits;
 	info.flow_ctrl = '0';
	info.port_fd = m_fd;
	info.stop_bit = stopbits;

    printf("baudrate:%d\n",info.baud_rate);
	if(set_port(&info)==-1 )
	{
		printf( "set port fail\n");
		return -1;
	}

    
	// �������ڵĽ����߳�
	res = pthread_attr_init(&attr);
	if( res!=0 )
	{
		printf("Create attribute failed\n" );
	}
	// �����̰߳�����
	res = pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );
	// �����̷߳�������
	res += pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
	if( res!=0 )
	{
		printf( "Setting attribute failed\n" );
	}

	this->m_ExitThreadFlag = 0;

	res = pthread_create( &m_thread, &attr, (void *(*) (void *))&ReceiveThreadFunc, (void*)this );
	if( res!=0 )
	{
		close( m_fd );
		return -1;
	}

	pthread_attr_destroy( &attr );
	return 0;
}

int CSerial::WritePort( char *Buf, int len )
{
	int  sendlen=0;

	sendlen=write(m_fd, Buf, len);

	if(sendlen==len)
	{
		printf("serial send success\n");
		usleep(50);
		tcdrain(m_fd);
		return sendlen;
	}
	else
	{
		//�������������
		printf("serial send failed\n");
		tcflush( m_fd,TCOFLUSH);
		return -1;
	}
}

// ���մ������ݴ�������
int CSerial::PackagePro( char* Buf, int len )
{
	// �Խ��յ����ݽ��д���������Ϊ�򵥵����ݻط�
	int i1;
	i1= WritePort(Buf, len );


	for( i1=0; i1<len; i1++ )
	{
		printf( "0x%x ",Buf[i1]);
	}
	printf( "\n" );
	/*

	Buf[0] = 0x02;
	Buf[1] = 0x03;
	Buf[2] = 0x00;
	Buf[3] = 0x01;
	Buf[4] = 0x02;
	Buf[5] = 0x03;
	Buf[6] = 0x00;
	Buf[7] = 0x55;
	Buf[8] = 0x58;

	len = 9;
	i1= WritePort(Buf, len );
	*/

	return i1;
}


int CSerial::ClosePort()
{
	m_ExitThreadFlag = 1;
	close( m_fd );
	return 0;
}
